package com.nowcoder.topic.dp.middle;

import java.util.ArrayList;

/**
 * NC302 环形数组的连续子数组最大和
 * @author d3y1
 */
public class NC302{
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param nums int整型ArrayList
     * @return int整型
     */
    public int maxSubarraySumCircular (ArrayList<Integer> nums) {
//        return solution1(nums);
        return solution2(nums);
//        return solution3(nums);
    }

    /**
     * 动态规划 + 前缀和: OOM
     *
     * dp[i][j]表示从第i个数到第j个数的子数组的和
     *
     * dp[i][j] = pre[j] - pre[i-1]  , 1<=i<=n i<=j<=n
     *
     * @param nums
     * @return
     */
    private int solution1(ArrayList<Integer> nums){
        int n = nums.size();

        // 前缀和
        int[] pre = new int[n+1];
        for(int i=1; i<=n; i++){
            pre[i] = pre[i-1] + nums.get(i-1);
        }

        int sum = pre[n];
        int max = Integer.MIN_VALUE;
        int[][] dp = new int[n+1][n+1];
        // for(int i=1; i<=n; i++){
        //     for(int j=1; j<=n; j++){
        //         if(i <= j){
        //             dp[i][j] = pre[j] - pre[i-1];
        //         }else{
        //             dp[i][j] = sum + (pre[j] - pre[i-1]);
        //         }
        //         max = Math.max(max, dp[i][j]);
        //     }
        // }
        for(int i=1; i<=n; i++){
            for(int j=i; j<=n; j++){
                dp[i][j] = pre[j] - pre[i-1];
                if(i==1 && j==n){
                    max = Math.max(max, dp[i][j]);
                }else{
                    max = Math.max(max, Math.max(dp[i][j],sum-dp[i][j]));
                }
            }
        }

        return max;
    }

    /**
     * 动态规划 + 前缀和
     *
     * dp[i]表示以数组中第i个数为结尾的连续子数组的最大和
     *
     * // 情形1 不考虑环形 结果为 中间某一连续部分
     * dp[i] = Math.max(dp[i-1],0) + nums.get(i-1)  , 1<=i<=n
     * max = Math.max(max, dp[i])
     *
     * // 情形2 考虑环形 结果为 前面最大连续部分 + 后面固定连续部分
     * max = Math.max(max, leftMax[i]+right[i])
     *
     * @param nums
     * @return
     */
    private int solution2(ArrayList<Integer> nums){
        int n = nums.size();

        // 前缀和
        int[] pre = new int[n+1];
        for(int i=1; i<=n; i++){
            pre[i] = pre[i-1] + nums.get(i-1);
        }

        int[] dp = new int[n+1];
        int max = Integer.MIN_VALUE;
        int sum = pre[n];
        int[] leftMax = new int[n+1];
        int[] right = new int[n+1];
        for(int i=1; i<=n; i++){
            // 情形1 不考虑环形 结果为 中间某一连续部分
//            if(dp[i-1] > 0){
//                dp[i] = dp[i-1] + nums.get(i-1);
//            }else{
//                dp[i] = nums.get(i-1);
//            }
            dp[i] = Math.max(dp[i-1],0) + nums.get(i-1);
            max = Math.max(max, dp[i]);

            // 情形2 考虑环形 结果为 前面最大连续部分 + 后面固定连续部分
            leftMax[i] = Math.max(leftMax[i-1], pre[i-1]);
            right[i] = sum - pre[i-1];
            max = Math.max(max, leftMax[i]+right[i]);
        }

        return max;
    }

    /**
     * 动态规划: 空间优化
     *
     * preMax = Math.max(preMax,0) + nums.get(i-1)
     * preMin = Math.min(preMin,0) + nums.get(i-1)
     *
     * max = Math.max(max, preMax)
     * min = Math.min(min, preMin)
     *
     * @param nums
     * @return
     */
    private int solution3(ArrayList<Integer> nums){
        int n = nums.size();

        int sum = 0;
        for(int i=1; i<=n; i++){
            sum += nums.get(i-1);
        }

        int max = Integer.MIN_VALUE;
        int min = Integer.MAX_VALUE;
        for(int i=1,preMin=0,preMax=0; i<=n; i++){
            // 情形1 不考虑环形 结果为 中间某一连续部分最大和 max
            preMax = Math.max(preMax,0) + nums.get(i-1);
            // 情形2 考虑环形 结果为 sum-中间某一连续部分最小和 sum-min
            preMin = Math.min(preMin,0) + nums.get(i-1);

            max = Math.max(max, preMax);
            min = Math.min(min, preMin);
        }

        // 特殊情况 全为负数
        if(max < 0){
            return max;
        }

        return Math.max(max, sum-min);
    }
}